package com.nyj.sqlSession;


import com.nyj.config.BoundSql;
import com.nyj.pojo.Configuration;
import com.nyj.pojo.MappedStatement;
import com.nyj.utils.GenericTokenParser;
import com.nyj.utils.ParameterMapping;
import com.nyj.utils.ParameterMappingTokenHandler;
import com.nyj.utils.TokenHandler;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author niuyj
 * @date 2020/5/2 1:26
 * @description
 */
public class SimpleExecutor implements Executor {

    private Connection connection = null;

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, NoSuchFieldException, IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
        //获取数据库连接
        connection = configuration.getDataSource().getConnection();
        //获取sql
        String sql = mappedStatement.getSql();
        //处理sql语句,将#{}形式的占位符，转换成？
        BoundSql boundSql = getBoundSql(sql);

        //获取预编译prepareStatement对象
        PreparedStatement preparedStatement = null;
        //定义List
        ArrayList<Object> objectsList = new ArrayList<>();
        String finalSql = boundSql.getSql();
        preparedStatement = connection.prepareStatement(finalSql);
        //设置参数
        //获取参数全路径
        String paramterType = mappedStatement.getParamterType();
        //获取参数类型
        Class<?> paramterClassType = getClassType(paramterType);
        //获取#{}中的值
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            //#{}中的值
            String content = parameterMapping.getContent();
            Field declaredField = paramterClassType.getDeclaredField(content);
            declaredField.setAccessible(true);
            //参数的值
            Object o = declaredField.get(params[0]);
            //给占位符赋值
            preparedStatement.setObject(i + 1, o);
        }
        //执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        //获取返回值路径
        String resultType = mappedStatement.getResultType();
        //获取返回值对象类型
        Class<?> resultClassType = getClassType(resultType);

        //封装返回结果集
        while (resultSet.next()) {
            //返回值对象实例
            Object resultInstance = resultClassType.newInstance();
            //获取元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取列数
            int columnCount = metaData.getColumnCount();
            //遍历获取列名，列值
            for (int i = 1; i <= columnCount; i++) {
                //列名
                String columnName = metaData.getColumnName(i);
                //获取列值
                Object value = resultSet.getObject(columnName);
                //使用反射或者内省，根据数据库表和实体的对应关系，完成封装
                //创建属性描述器
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultClassType);
                //获取写方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                //写入属性值
                writeMethod.invoke(resultInstance, value);

            }
            objectsList.add(resultInstance);

        }


        return (List<E>) objectsList;
    }

    @Override
    public int doUpdate(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, NoSuchFieldException, IllegalAccessException {

        //获取数据库连接
        connection = configuration.getDataSource().getConnection();
        //获取sql
        String sql = mappedStatement.getSql();
        //处理sql语句,将#{}形式的占位符，转换成？
        BoundSql boundSql = getBoundSql(sql);

        //获取预编译prepareStatement对象
        PreparedStatement preparedStatement = null;
        //定义List
        ArrayList<Object> objectsList = new ArrayList<>();
        String finalSql = boundSql.getSql();
        preparedStatement = connection.prepareStatement(finalSql);
        //设置参数
        //获取参数全路径
        String paramterType = mappedStatement.getParamterType();
        //获取参数类型
        Class<?> paramterClassType = getClassType(paramterType);
        //获取#{}中的值
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            //#{}中的值
            String content = parameterMapping.getContent();
            Field declaredField = paramterClassType.getDeclaredField(content);
            declaredField.setAccessible(true);
            //参数的值
            Object o = declaredField.get(params[0]);
            //给占位符赋值
            preparedStatement.setObject(i + 1, o);
        }

        int i = preparedStatement.executeUpdate();

        return i;
    }

    private Class<?> getClassType(String paramterType) {
        if (paramterType != null) {
            Class<?> aClass = null;
            try {
                aClass = Class.forName(paramterType);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return aClass;
        }
        return null;
    }

    //完成对#{}的解析工作，将#{}用？代替，将#{}中的值存储
    private BoundSql getBoundSql(String sql) {
        //标记处理类，主要配合通用解析器GennericTokenParser类完成对配置文件等的解析工作，其中TokenHandler主要完成处理
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        //GenericTokenParser，通用解析器，完成了代码中占位符的解析，然后再根据给定的标记处理器（TokenHandler）,来进行表达式的处理
        //三个参数openToken：开始标记，closeToken：结束标记，handler：标记处理标记
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析sql语句
        String parseSql = genericTokenParser.parse(sql);
        //获取#{}中的值
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        BoundSql boundSql = new BoundSql(parseSql, parameterMappings);

        return boundSql;


    }
}
